﻿// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#if NET451 || NETSTANDARD2_0 || NETCOREAPP3_1_OR_GREATER
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;

#if ASPNETCORE1
namespace WebMarkupMin.AspNetCore1.Helpers
#elif ASPNETCORE2
namespace WebMarkupMin.AspNetCore2.Helpers
#elif ASPNETCORE3
namespace WebMarkupMin.AspNetCore3.Helpers
#elif ASPNETCORE5
namespace WebMarkupMin.AspNetCore5.Helpers
#elif ASPNETCORE6
namespace WebMarkupMin.AspNetCore6.Helpers
#elif ASPNETCORE7
namespace WebMarkupMin.AspNetCore7.Helpers
#elif ASPNETCORE8
namespace WebMarkupMin.AspNetCore8.Helpers
#else
#error No implementation for this target
#endif
{
	/// <summary>
	/// Provides a simple IAsyncResult that wraps a Task
	/// </summary>
	/// <remarks>
	/// We could use the Task as the IAsyncResult if the Task's AsyncState is the same as the object state,
	/// but that's very rare, in particular in a situation where someone cares about allocation, and always
	/// using TaskAsyncResult simplifies things and enables additional optimizations.
	/// </remarks>
	internal sealed class TaskAsyncResult : IAsyncResult
	{
		/// <summary>
		/// Callback to invoke when the wrapped task completes
		/// </summary>
		private readonly AsyncCallback _callback;

		/// <summary>
		/// The wrapped Task
		/// </summary>
		internal readonly Task _task;

		/// <summary>
		/// Gets a user-defined object that qualifies or contains information about an asynchronous operation
		/// </summary>
		public object AsyncState
		{
			get;
		}

		/// <summary>
		/// Gets a value that indicates whether the asynchronous operation completed synchronously
		/// </summary>
		/// <remarks>
		/// This is set lazily based on whether the <see cref="_task"/> has completed by the time this object is created.
		/// </remarks>
		public bool CompletedSynchronously
		{
			get;
		}

		/// <summary>
		/// Gets a value that indicates whether the asynchronous operation has completed
		/// </summary>
		public bool IsCompleted => _task.IsCompleted;

		/// <summary>
		/// Gets a <see cref="WaitHandle"/> that is used to wait for an asynchronous operation to complete
		/// </summary>
		public WaitHandle AsyncWaitHandle => ((IAsyncResult)_task).AsyncWaitHandle;


		/// <summary>
		/// Initializes the IAsyncResult with the Task to wrap and the associated object state
		/// </summary>
		/// <param name="task">The Task to wrap</param>
		/// <param name="state">The new AsyncState value</param>
		/// <param name="callback">Callback to invoke when the wrapped task completes</param>
		internal TaskAsyncResult(Task task, object state, AsyncCallback callback)
		{
			Debug.Assert(task != null);

			_task = task;
			AsyncState = state;

			if (task.IsCompleted)
			{
				// Synchronous completion. Invoke the callback. No need to store it.
				CompletedSynchronously = true;
				callback?.Invoke(this);
			}
			else if (callback != null)
			{
				// Asynchronous completion, and we have a callback; schedule it. We use OnCompleted rather than ContinueWith in
				// order to avoid running synchronously if the task has already completed by the time we get here but still run
				// synchronously as part of the task's completion if the task completes after (the more common case).
				_callback = callback;
				_task.ConfigureAwait(continueOnCapturedContext: false)
					.GetAwaiter()
					.OnCompleted(InvokeCallback) // allocates a delegate, but avoids a closure
					;
			}
		}

		/// <summary>
		/// Invokes the callback
		/// </summary>
		private void InvokeCallback()
		{
			Debug.Assert(!CompletedSynchronously);
			Debug.Assert(_callback != null);

			_callback.Invoke(this);
		}
	}
}
#endif